package com.dries.portal.module.user.service.impl;

import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dries.common.api.CommonResult;
import com.dries.common.api.ResultCode;
import com.dries.common.exception.ApiException;
import com.dries.common.exception.Asserts;
import com.dries.portal.module.user.bo.PortalUserDetails;
import com.dries.portal.module.user.mapper.UserMapper;
import com.dries.portal.module.user.entity.UserEntity;
import com.dries.portal.module.user.enums.LoginType;
import com.dries.portal.module.user.form.RegisterForm;
import com.dries.portal.module.user.form.UserLoginForm;
import com.dries.portal.module.user.service.UserCacheService;
import com.dries.portal.module.user.service.UserService;
import com.dries.security.util.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@DS("master")
@Slf4j
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserCacheService userCacheService;

    @Value("${redis.key.authCode}")
    private String REDIS_KEY_PREFIX_AUTH_CODE;

    @Value("${redis.expire.authCode}")
    private Long AUTH_CODE_EXPIRE_SECONDS;

    @Override
    public UserDetails loadUserByUsername(String username) {

        UserEntity user = getByUsername(username).getData();

        if (user != null) {
            return new PortalUserDetails(user);
        }

        throw new UsernameNotFoundException("用户名或密码错误");
    }

    @Override
    public UserDetails loadUserByOpenId(String openId) {

        UserEntity user = getUserByOpenId(openId);

        if (user != null) {
            return new PortalUserDetails(user);
        }

        throw new UsernameNotFoundException("用户名或密码错误");
    }


    /**
     * 根据用户ID获取会员
     *
     * @param userId
     */
    @Override
    public CommonResult<UserEntity> getUserById(Long userId) {
        UserEntity userEntity = userMapper.selectById(userId);
        return CommonResult.success(userEntity);
    }

    @Override
    public UserEntity getUserByOpenId(String openId) {

        UserEntity user = userCacheService.getUserByOpenId(openId);

        if (user != null) {
            return user;
        }

        List<UserEntity> list = new LambdaQueryChainWrapper<>(userMapper)
                .eq(UserEntity::getUnionId, openId)
                .list();

        if (list != null && list.size() == 1) {
            user = list.get(0);
            user.setLoginType(LoginType.WX_LOGIN.getCode());
            userCacheService.setUserByOpenId(user);
            return user;
        } else return null;

    }

    @Override
    public CommonResult<UserEntity> getByUsername(String username) {

        UserEntity user = userCacheService.getUser(username);

        if (user != null) {
            return CommonResult.success(user);
        }

        List<UserEntity> list =
                new LambdaQueryChainWrapper<>(userMapper).eq(UserEntity::getUsername, username).list();

        if (list != null && list.size() > 0) {
            user = list.get(0);
            user.setLoginType(LoginType.USERNAME_LOGIN.getCode());
            userCacheService.setUser(user);
            return CommonResult.success(user);
        }

        return CommonResult.failed();
    }

    /**
     * 刷新token
     *
     * @param token
     */
    @Override
    public String refreshToken(String token) {
        return jwtTokenUtil.refreshHeadToken(token);
    }

    /**
     * 获取当前登录会员
     */
    @Override
    public CommonResult<UserEntity> getCurrentUser() {

        SecurityContext context = SecurityContextHolder.getContext();
        Authentication auth = context.getAuthentication();
        PortalUserDetails userDetails = (PortalUserDetails) auth.getPrincipal();

        return CommonResult.success(userDetails.getUser());
    }

    /**
     * 用户登录
     *
     * @param loginForm
     */
    @Override
    public CommonResult login(UserLoginForm loginForm) {
        String token = null;
        // 密码需要客户端加密后传递
        try {

            UserDetails userDetails = loadUserByUsername(loginForm.getUsername());
            if (!passwordEncoder.matches(loginForm.getPassword(), userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
            throw new ApiException(ResultCode.LOGIN_FAILED);
        }

        return CommonResult.success(token);
    }

    @Override
    public String loginByOpenId(String openId) {
        String token = null;

        try {
            UserDetails userDetails = loadUserByOpenId(openId);
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
            throw new ApiException(ResultCode.LOGIN_FAILED);
        }

        return token;
    }

    /**
     * 用户注册
     *
     * @param registerForm
     */
    @Transactional
    @Override
    public CommonResult registerUser(RegisterForm registerForm) {

        String currentTime = String.valueOf(System.currentTimeMillis());
        String userId = RandomUtil.randomNumbers(6) + currentTime;
//        String userId = uuid + currentTime;
        log.info("当前时间： {}，用户ID： {}", currentTime, userId);

        List<UserEntity> list =
                new LambdaQueryChainWrapper<>(userMapper)
                        .eq(UserEntity::getUsername, registerForm.getUsername())
                        .or()
                        .eq(UserEntity::getMobile, registerForm.getMobile())
                        .or()
                        .eq(UserEntity::getUserid, userId)
                        .list();

        if (CollUtil.isNotEmpty(list)) {
            Asserts.fail("该用户已存在");
        }

        UserEntity user = new UserEntity();
        BeanUtils.copyProperties(registerForm, user);
        user.setUserid(Long.valueOf(userId));
        user.setPassword(passwordEncoder.encode(registerForm.getPassword()));
        user.setWrtime(new Date());

        int insertResult = userMapper.insert(user);
        if (insertResult == 1) {
            return CommonResult.success(null, "注册成功");
        } else {
            return CommonResult.failed("注册失败");
        }
    }

    /**
     * 微信用户注册
     *
     * @param userInfo
     */
    @Transactional
    @Override
    public CommonResult register(WxMaUserInfo userInfo) {

        String currentTime = String.valueOf(System.currentTimeMillis());
        String userId = RandomUtil.randomNumbers(6) + currentTime;

        String token;
        List<UserEntity> list = new LambdaQueryChainWrapper<>(userMapper)
                .eq(UserEntity::getUsername, userInfo.getNickName())
                .or()
                .eq(UserEntity::getUserid, userId)
                .or()
                .eq(UserEntity::getOpenid, userInfo.getOpenId())
                .or()
                .eq(UserEntity::getUnionId, userInfo.getUnionId())
                .list();

        if (CollUtil.isNotEmpty(list)) {
            UserEntity user = list.get(0);
            user.setOpenid(userInfo.getOpenId());
            user.setUnionId(userInfo.getUnionId());
            boolean result = saveOrUpdate(user);
            if (result) {
                user = getUserByOpenId(userInfo.getUnionId());
                token = loginByOpenId(userInfo.getUnionId());
                Map<String, Object> map = new HashMap<>();
                map.put("token", token);
                map.put("userInfo", user);
                return CommonResult.success(map);
            }
        } else {
            UserEntity registerUser = new UserEntity();
            String password = IdUtil.simpleUUID();
            // TODO 用户初始密码
            log.info("用户初始密码{}", password);
            registerUser.setOpenid(userInfo.getOpenId());
            registerUser.setUnionId(userInfo.getUnionId());
            registerUser.setUsername(userInfo.getNickName());
            registerUser.setImghead(userInfo.getAvatarUrl());
            registerUser.setSex(Integer.valueOf(userInfo.getGender()));
            registerUser.setPassword(passwordEncoder.encode(password));
            registerUser.setAddrdefault(password);
            registerUser.setFlag(2);
            registerUser.setWrtime(new Date());
            boolean save = saveOrUpdate(registerUser);

            if (save) {
                registerUser = getUserByOpenId(userInfo.getUnionId());
                token = loginByOpenId(userInfo.getUnionId());
                Map<String, Object> map = new HashMap<>();
                map.put("token", token);
                map.put("userInfo", registerUser);
                return CommonResult.success(map);
            } else return CommonResult.failed("授权失败！");
        }
        return CommonResult.failed("授权失败！");
    }
}
